/**
 * 
 */
package wpmp.security.mgr.service;

import java.sql.Timestamp;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.Criteria;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.type.StandardBasicTypes;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.support.rowset.SqlRowSet;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import wcommons.lang.DateUtils;
import wcommons.lang.NumberUtils;
import wcommons.lang.UrlUtils;
import wcommons.lang.quickbean.Message;
import wpmp.security.mgr.base.BaseService;
import wpmp.security.mgr.dao.ResourceDao;
import wpmp.security.mgr.dao.RoleResourceRelaDao;
import wpmp.security.mgr.entity.Resource;
import wpmp.security.mgr.entity.RoleResourceRela;
import wpmp.utils.Statics;

/**
 * @author Wayne.Wang<5waynewang@gmail.com>
 * @since 10:02:13 AM Mar 18, 2013
 */
@Component
@Transactional(rollbackFor = Exception.class)
public class ResourceService extends BaseService {

	// private static final Logger LOG =
	// LoggerFactory.getLogger(ResourceService.class);

	// 需要更新的字段
	private static final String[] UPDATE_PROPERTIES = { "name", "isLeaf", "url", "isVisible", "isPublic", "memo",
			"needLogin", "paraUrl", "actionName" };

	private static final String SQL_EXISTS_PARENT = "EXISTS(SELECT 1 FROM sys_resource WHERE parent_id={alias}.id AND id=?)";

	@Autowired
	private ResourceDao dao;
	@Autowired
	private RoleResourceRelaDao roleResourceRelaDao;

	public ResourceDao getDao() {
		return dao;
	}

	public Resource get(Long id) {
		Resource entity = dao.get(id);
		if (entity == null) {
			return entity;
		}
		Resource result = new Resource();
		BeanUtils.copyProperties(entity, result);
		return result;
	}

	public Resource getParent(Long id) {
		return dao.queryForObject(Restrictions.sqlRestriction(SQL_EXISTS_PARENT, id, StandardBasicTypes.LONG));
	}

	public List<Resource> getChildren(Long id) {
		Criteria criteria = dao.createCriteria(Restrictions.eq("parentId", id));
		criteria.addOrder(Order.asc("orderNum"));
		return dao.queryForList(criteria);
	}

	/**
	 * 获取子角色最大的排序号
	 * @param id
	 * @return
	 */
	public int getMaxChildOrderNum(Long id) {
		Integer maxOrderNum = (Integer) dao.createCriteria(Restrictions.eq("parentId", id))
				.setProjection(Projections.max("orderNum")).uniqueResult();
		return maxOrderNum == null ? 0 : maxOrderNum.intValue();
	}

	public Message validateName(Resource entity) {
		Criteria criteria = dao.createCriteria(Restrictions.eq("parentId", entity.getParentId()),
				Restrictions.eq("name", entity.getName()));
		if (NumberUtils.gtZero(entity.getId())) {
			criteria.add(Restrictions.ne("id", entity.getId()));
		}
		if (dao.queryForInt(criteria) > 0) {
			return Message.getErrorMessage("同级别下已存在资源名称[" + entity.getName() + "]");
		}
		return Message.getDefaultMessage();
	}

	public Message insert(Resource entity) {
		Message message = validateName(entity);
		if (message.notpass()) {
			return message;
		}

		Timestamp currentTime = new Timestamp(System.currentTimeMillis());
		if (StringUtils.isBlank(entity.getUrl())) {
			entity.setUrl("/");
		}
		entity.setOrderNum(getMaxChildOrderNum(entity.getParentId()) + 1);
		entity.setCreateTime(currentTime);
		entity.setCreateUser(entity.getSysLoginName());

		setUrls(entity);

		entity.setUpdateTime(currentTime);
		entity.setUpdateUser(entity.getSysLoginName());
		dao.save(entity);

		final Resource parent = dao.get(entity.getParentId());
		entity.setLevel(parent.getLevel() + entity.getId() + ".");
		dao.save(entity);

		// 新增与操作类型的关系
		if (ArrayUtils.isNotEmpty(entity.getOperationIds())) {
			dao.insertResourceOperationRela(entity.getId(), entity.getOperationIds());
		}

		message.putAttribute("id", entity.getId());
		message.putAttribute("name", entity.getName());
		message.putAttribute("isLeaf", entity.getIsLeaf());
		message.putAttribute("orderNum", entity.getOrderNum());
		return message;
	}

	public Message update(Resource entity) {
		Message message = validateName(entity);
		if (message.notpass()) {
			return message;
		}

		if (StringUtils.isBlank(entity.getUrl())) {
			entity.setUrl("/");
		}

		Resource result = dao.get(entity.getId());

		String name = result.getName();
		String url = result.getUrl();
		Boolean isLeaf = result.getIsLeaf();

		wcommons.beanutils.BeanUtils.copyProperties(result, entity, UPDATE_PROPERTIES, false);

		setUrls(result);

		result.setUpdateTime(DateUtils.newTimestamp());
		result.setUpdateUser(entity.getSysLoginName());
		dao.save(result);

		// 非叶子节点修改URL，同时更改其下所有子节点的baseUrl
		if (!isLeaf && !result.getIsLeaf() && !StringUtils.equals(url, result.getUrl())) {
			resetChildUrl(result.getLevel(), url, result.getUrl());
		}

		if (ArrayUtils.isEmpty(entity.getOperationIds())) {
			// 清空资源、操作类型之间的关系
			dao.deleteResourceOperationRela(entity.getId());
			// 现有的 角色、资源之间的关系
			List<RoleResourceRela> roleResourceRelas = roleResourceRelaDao.queryForList(Restrictions.eq("resourceId",
					entity.getId()));
			if (CollectionUtils.isNotEmpty(roleResourceRelas)) {
				Long[] relaIds = new Long[roleResourceRelas.size()];
				for (int i = 0; i < relaIds.length; i++) {
					relaIds[i] = roleResourceRelas.get(i).getId();
				}
				// 删除角色、资源、操作类型之间的关系
				roleResourceRelaDao.deleteOperationRela(relaIds);
				// 删除角色、资源之间的关系
				roleResourceRelaDao.delete(roleResourceRelas);
			}
		}
		else {
			// 清空资源、操作类型之间的关系
			dao.deleteResourceOperationRela(entity.getId());
			// 新增资源、操作类型之间的关系
			dao.insertResourceOperationRela(entity.getId(), entity.getOperationIds());

			// 查询角色、资源、操作类型之间的关系
			SqlRowSet rs = dao.queryRoleResourceOperationRela(entity.getId());
			// 需要新增的操作类型
			List<Long> insertOperationIds = new LinkedList<Long>();
			out: for (Long operationId : entity.getOperationIds()) {
				rs.beforeFirst();
				while (rs.next()) {
					if (operationId.longValue() == rs.getLong("operation_id")) {
						continue out;
					}
				}
				insertOperationIds.add(operationId);
			}

			// 需要删除的操作类型
			List<Long> deleteOperationIds = new LinkedList<Long>();
			List<Long> deleteRelaIds = new LinkedList<Long>();
			rs.beforeFirst();
			out: while (rs.next()) {
				for (Long operationId : entity.getOperationIds()) {
					if (operationId.longValue() == rs.getLong("operation_id")) {
						continue out;
					}
				}
				deleteOperationIds.add(rs.getLong("operation_id"));
				deleteRelaIds.add(rs.getLong("rela_id"));
			}

			if (CollectionUtils.isNotEmpty(insertOperationIds)) {
				// 新增资源、操作类型之间的关系
				dao.insertResourceOperationRela(entity.getId(), insertOperationIds);
			}

			if (CollectionUtils.isNotEmpty(deleteOperationIds)) {
				// 删除角色、资源、操作类型之间的关系
				dao.deleteRoleResourceOperationRela(deleteRelaIds, deleteOperationIds);
			}
		}

		message.putAttribute("id", entity.getId());
		message.putAttribute("isLeaf", entity.getIsLeaf());
		if (!entity.getName().equals(name)) {
			message.putAttribute("name", entity.getName());
		}
		return message;
	}

	void setUrls(Resource entity) {
		final String[] arr;
		if (StringUtils.startsWith(entity.getUrl(), wpmp.security.mgr.Statics.HTTP_URL_PREFIX)) {
			arr = UrlUtils.splitUrlParams(UrlUtils.spliceParams(entity.getUrl(), entity.getParaUrl()));
		}
		else {
			String bgnUrl = null;
			if (NumberUtils.gtZero(entity.getParentId())) {
				Resource parent = dao.get(entity.getParentId());
				if (parent != null) {
					bgnUrl = parent.getBaseUrl();
				}
			}
			arr = UrlUtils.splitUrlParams(UrlUtils.spliceParams(UrlUtils.spliceUrls(bgnUrl, entity.getUrl()),
					entity.getParaUrl()));
		}
		entity.setBaseUrl(arr[0]);

		final Map<String, String> result = UrlUtils.getStringParamMap(arr[1]);

		final String action = result.get(entity.getActionName());

		entity.setBaseAction(StringUtils.defaultIfBlank(action, Statics.DEFAULT_ACTION));
	}

	void resetChildUrl(String parentLevel, String fromUrl, String toUrl) {
		fromUrl = StringUtils.trimToEmpty(fromUrl);
		toUrl = StringUtils.trimToEmpty(toUrl);
		if (StringUtils.equals(fromUrl, toUrl)) {
			return;
		}

		Criteria criteria = dao.createCriteria(Restrictions.like("level", parentLevel, MatchMode.START),
				Restrictions.ne("level", parentLevel));
		criteria.addOrder(Order.asc("level"));
		List<Resource> entities = dao.queryForList(criteria);
		if (CollectionUtils.isEmpty(entities)) {
			return;
		}
		for (Resource entity : entities) {
			setUrls(entity);
		}
		dao.save(entities);
	}

	public Message delete(Long id) {
		Resource entity = dao.get(id);

		// 重新排序
		dao.decreaseOrderNumWhenGt(entity.getParentId(), entity.getOrderNum());

		List<Resource> entities = dao.queryForList(Restrictions.like("level", entity.getLevel(), MatchMode.START));
		Long[] ids = new Long[entities.size()];
		for (int i = 0; i < ids.length; i++) {
			ids[i] = entities.get(i).getId();
		}
		// 删除资源、操作类型之间的关系
		dao.deleteResourceOperationRela(ids);
		// 删除角色、资源、操作之间的关系
		dao.deleteRoleResourceOperationRela(ids);
		// 删除角色、资源之间的关系
		dao.deleteRoleResourceRela(ids);
		// 删除角色
		dao.delete(entities);
		return Message.getDefaultMessage();
	}

	public Message sort(Resource entity) {
		Resource result = dao.get(entity.getId());
		// 父类没变
		if (NumberUtils.eq(result.getParentId(), entity.getParentId())) {
			// 5 > 1
			if (entity.getOrderNum() > result.getOrderNum()) {
				dao.decreaseOrderNumWhenBetween(entity.getParentId(), result.getOrderNum(), entity.getOrderNum());
			}
			// 1 < 5
			else if (entity.getOrderNum() < result.getOrderNum()) {
				dao.increaseOrderNumWhenBetween(entity.getParentId(), entity.getOrderNum(), result.getOrderNum());
			}
			result.setOrderNum(entity.getOrderNum());
			dao.save(result);
		}
		// 父类变了
		else {
			// 原父类下的排序变动
			dao.decreaseOrderNumWhenGt(result.getParentId(), result.getOrderNum());

			int maxOrderNum = getMaxChildOrderNum(entity.getParentId());

			if (entity.getOrderNum() == null || entity.getOrderNum() > maxOrderNum) {
				entity.setOrderNum(maxOrderNum + 1);
			}
			if (entity.getOrderNum() <= 0) {
				entity.setOrderNum(1);
			}
			if (entity.getOrderNum() <= maxOrderNum) {
				// 递增排序号，腾出空间
				dao.increaseOrderNumWhenGe(entity.getParentId(), entity.getOrderNum());
			}

			final String level = result.getLevel();

			final Resource orgnParent = dao.get(result.getParentId());

			result.setOrderNum(entity.getOrderNum());
			result.setLevel(orgnParent.getLevel() + result.getId() + ".");
			result.setParentId(entity.getParentId());

			setUrls(result);

			dao.save(result);

			// 更新子类的level
			dao.updateChildLevel(level, result.getLevel());

			Resource parent = dao.get(result.getParentId());
			// 父类URL改变，更改其下所有子节点的baseUrl
			if (!StringUtils.equals(orgnParent.getUrl(), parent.getUrl())) {
				resetChildUrl(result.getLevel(), orgnParent.getUrl(), parent.getUrl());
			}
		}

		return Message.getDefaultMessage();
	}

	/**
	 * <pre>
	 * 根据用户名查询该用户有权限的操作
	 * {resource_id,operation_id,level,is_public}
	 * </pre>
	 * @param loginName
	 * @return
	 */
	public SqlRowSet queryUserResourceOperationForRowSet(String loginName) {
		return dao.queryUserResourceOperationForRowSet(loginName);
	}
}
